#include "StdAfx.h"
#include "WindowDataGrabber.h"
#include <process.h>
#include "Observers.h"


WindowDataGrabber::WindowDataGrabber()
{
	Thread = 0;
}


//-----------------------------------------------------------------------------------------------------------------------
bool WindowDataGrabber::Start(UINT iInterval, bool iUsePrintWindow)
{
	Interval = iInterval;
	UsePw = iUsePrintWindow;


	//check input
	if(!iInterval)
		return false;
	if(Receivers.size() == 0) 
		return false;


	if(Thread == 0)
	{
		//create start event
		StartEvent = CreateEvent(0, 0, 0, _T("screen_start_event"));


		//begin watching
		uintptr_t res = _beginthreadex(0, 0, WatcherFunction, (void* )this, 0, 0);	
		bool succ = res != -1L;
		Thread = (HANDLE)res;


		//wait for starting	
		WaitForSingleObject(StartEvent, 3000);
		CloseHandle(StartEvent);
		return succ;
	}
	return true;
}


//-----------------------------------------------------------------------------------------------------------------------
void WindowDataGrabber::Stop()
{
	//terminate thread
	if(Thread != 0)
	{
		::QueueUserAPC(TerminateProc, Thread, 0);
		if(WaitForSingleObject(Thread, 10000) == WAIT_TIMEOUT)
			TerminateThread(Thread, 1);
		Thread = 0;
	}
}


//------------------------------------------------------------------------------------------------------------------------------------------
void WindowDataGrabber::AddReceiver(HWND iHwnd, IScreenShotReceiver *iReceiver)
{
	SyncObject.LockWriter();
	Receivers.push_back(HwndReceiver(iHwnd, iReceiver));
	SyncObject.UnlockWriter();
}


//------------------------------------------------------------------------------------------------------------------------------------------
void WindowDataGrabber::RemoveReceiver(HWND iHwnd)
{
	SyncObject.LockWriter();
	std::deque<HwndReceiver>::iterator it = Receivers.begin();

	//find handle in deque and erase it
	for(it=Receivers.begin(); it!=Receivers.end(); ++it)
	{
		if(it->first == iHwnd)
		{
			Receivers.erase(it);
			break;
		}
	}
	SyncObject.UnlockWriter();
}


//------------------------------------------------------------------------------------------------------------------------------------------
void WindowDataGrabber::RemoveReceiver(IScreenShotReceiver *iReceiver)
{
	SyncObject.LockWriter();
	std::deque<HwndReceiver>::iterator it = Receivers.begin();

	//find handle in deque and erase it
	for(it=Receivers.begin(); it!=Receivers.end(); ++it)
	{
		if(it->second == iReceiver)
		{
			Receivers.erase(it);
			break;
		}
	}
	SyncObject.UnlockWriter();
}


//------------------------------------------------------------------------------------------------------------------------------------------
unsigned int __stdcall WindowDataGrabber::WatcherFunction(void *iParams)
{
	if(!iParams)
		return 0;

	//get class object
	WindowDataGrabber *grabber = (WindowDataGrabber*)iParams;

	//send start event
	SetEvent(grabber->StartEvent);


	//main loop
	int interval = grabber->Interval;

	if(interval == 0) return 0;
	
	if(!grabber->UsePw)
		interval -= 50;
	if(interval < 0) interval = 1; 	

	while(1)
	{	
		grabber->SyncObject.LockReader();
		for(int i=0; i<grabber->Receivers.size(); i++)
		{
			//invalid handle
			HWND h = grabber->Receivers[i].first;
			IScreenShotReceiver *receiver = grabber->Receivers[i].second;

			//remove bad window
			if(!h || !IsWindow(h))
			{
				ObserverManager::GetInstance().UpdateTable(h, 0, 0, _T("Can't find the table"));
				grabber->Receivers[i].first = 0;
				continue;
			}


			//grab caption
			TCHAR buf[500];		
			GetWindowText(h, buf, 500);
			if(receiver)
				receiver->ReceiveCaption(buf);
			

			//need to terminate thread?			
			if(::SleepEx(1, TRUE) == WAIT_IO_COMPLETION)
				return 0;

			if(!grabber->UsePw)
			{
				//check if top window
				if(GetTopWindow(0) != h)
				{
					//bring window to front
					bool res = SetForegroundWindow(h);
					Sleep(50); //wait repainting..
					interval = grabber->Interval - 50;
					if(interval < 0) interval = 1; 	
				}
				else
					interval = grabber->Interval;
			}


			HDC win_dc = GetDC(h);
			RECT r;
			GetClientRect(h, &r);
			HDC hdc_comp = CreateCompatibleDC(0);
			if(!hdc_comp)
				continue;
			
			HBITMAP screen_bmp = CreateCompatibleBitmap(win_dc, r.right-r.left, r.bottom-r.top);
			if(!screen_bmp)
				continue;

			if(!SelectObject(hdc_comp, screen_bmp))
				continue;

			
			if(grabber->UsePw)
			{
				if(!PrintWindow(h, hdc_comp, 0))
					continue;
			}
			else
			{
				if(!BitBlt(hdc_comp, 0,0, r.right-r.left, r.bottom-r.top, win_dc, 0,0, SRCCOPY))
					continue;
			}
			
			
			ReleaseDC(h, win_dc);
			DeleteDC(hdc_comp);

			//need to terminate thread?			
			if(::SleepEx(1, TRUE) == WAIT_IO_COMPLETION)
				return 0;	

 			if(receiver)
				receiver->ReceiveScreenShot(screen_bmp);
		}
		grabber->SyncObject.UnlockReader();

		//need to terminate thread?			
		if(::SleepEx(interval, TRUE) == WAIT_IO_COMPLETION)
			return 0;
	}	
	return 0;
}


//------------------------------------------------------------------------------------------------------------------------------------------
void CALLBACK WindowDataGrabber::TerminateProc(ULONG_PTR dwParam )
{
	return;
}



//------------------------------------------------------------------------------------------------------------------------------------------
WindowDataGrabber::~WindowDataGrabber()
{
	Stop();
}